/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ca.trader.beans.enterprise;

import ca.trader.except.LocationNotFoundException;
import ca.trader.except.SaleAlreadyFavouritedByUserException;
import ca.trader.except.SaleDoesNotExistException;
import ca.trader.except.SaleNotFavouritedByUserException;
import ca.trader.except.UserDoesNotExistException;
import ca.trader.except.UserNotAuthenticatedException;
import ca.trader.logger.TraderLogger;
import ca.trader.regions.Country;
import ca.trader.regions.Location;
import ca.trader.regions.Region;
import ca.trader.sale.Sale;
import ca.trader.sale.SaleCategoryEnum;
import ca.trader.sale.SaleEntityConsts;
import ca.trader.sale.SaleLookupQuery;
import ca.trader.sale.SaleLookupResult;
import ca.trader.user.User;
import ca.trader.utils.HttpUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.StringUtils;

/**
 * Stateless bean for performing actions on Sale entity objects
 *
 * @author Ahsan
 */
@Stateless
public class SaleBean implements SaleBeanLocal
{

	 private static final String _className = SaleBean.class.getName();
	 /**
	  * Entity manager for persisting or retrieving sale objects
	  */
	 @PersistenceContext(unitName = TraderPersistenceConsts.PERSISTENCE_UNIT_NAME, type = PersistenceContextType.TRANSACTION)
	 private EntityManager entityManager;
	 

	 /**
	  * inserts a sale entity bean into the database. Throws
	  * IllegalArgumentException if the bean is null
	  *
	  * @param sale the sale entity bean to insert
	  * @exception IllegalArgumentException if the sale entity bean is null
	  */
	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public void insertSale(Sale sale)
	 {
		  String _methodName = "insertSale";

		  TraderLogger.logMethodEntry(_className, _methodName);

		  TraderLogger.logInfo(_className, _methodName, "Attempting sale insert.");

		  // if the sale entity object is null, throw IllegalArgumentException
		  if (sale == null)
		  {
			   TraderLogger.logWarning(_className, _methodName, "Sale is null. Exiting");
			   TraderLogger.logMethodExit(_className, _methodName);
			   throw new IllegalArgumentException("Sale is null");
		  }

		  // persist the sale
		  entityManager.persist(sale);

		  TraderLogger.logInfo(_className, _methodName, "Successfully inserted new sale into the sale list");

		  TraderLogger.logMethodExit(_className, _methodName);
	 }

	 /**
	  * Gets a list of sales created by a seller within a given range
	  *
	  * @param seller the user that owns the sales
	  * @param offset the index (starting with 0) to start searching at
	  * @param limit the maximum result count to get
	  * @return the list of sales
	  */
	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public List<Sale> getSalesBySeller(User user, int offset, int limit)
	 {
		  String _methodName = "getSalesByUser";

		  TraderLogger.logMethodEntry(_className, _methodName);


		  if (user == null)
		  {
			   TraderLogger.logWarning(_className, _methodName, "user is null.");
			   throw new IllegalArgumentException("User is null");
		  }

		  TraderLogger.logMethodExit(_className, _methodName);

		  return getSalesBySellerID(user.getId(), offset, limit);
	 }

	 /**
	  * Gets the sale created by the seller whose ID is provided
	  *
	  * @param sellerID the ID of the seller
	  * @param offset the index (starting with 0) to start searching at
	  * @param limit the maximum result count to get
	  * @return the list of sales. returns an empty list if no user found with
	  * the id.
	  */
	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public List<Sale> getSalesBySellerID(Integer userId, int offset, int limit)
	 {
		  String _methodName = "getSalesByUserID";

		  TraderLogger.logMethodEntry(_className, _methodName);

		  TraderLogger.logInfo(_className, _methodName, "Finding sales by user id: " + userId);

		  if (userId == null)
		  {
			   TraderLogger.logWarning(_className, _methodName, "userId is null.");
			   throw new IllegalArgumentException("userId is null");
		  }

		  TraderLogger.logInfo(_className, _methodName, String.format("Finding sales by user id: %d, offset = %d, limit = %d ", userId, offset, limit));

		  // Prepare query to find sale by seller
		  TypedQuery<Sale> querySalesBySeller = entityManager.createNamedQuery(SaleEntityConsts.NAMED_QUERY_FIND_SALE_BY_SELLER, Sale.class);
		  querySalesBySeller.setParameter(SaleEntityConsts.NAMED_QUERY_FIND_SALE_BY_USER_PARAM_SELLER_ID, userId);
		  querySalesBySeller.setFirstResult(offset);
		  querySalesBySeller.setMaxResults(limit);

		  List<Sale> saleList = querySalesBySeller.getResultList();

		  TraderLogger.logInfo(_className, _methodName, "Number of sales found: " + saleList.size());

		  TraderLogger.logMethodExit(_className, _methodName);

		  return saleList;
	 }

	 /**
	  * Gets sale for the authenticated user
	  *
	  * @param session The HttpSession object
	  * @param offset the index (starting with 0) to start searching at
	  * @param limit the maximum result count to get
	  * @return the list of sales
	  * @throws UserNotAuthenticatedException if no authenticated user exists
	  */
	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public List<Sale> getSalesByAuthenticatedUser(HttpSession session, int offset, int limit) throws UserNotAuthenticatedException
	 {

		  return getSalesBySellerID(HttpUtils.getAuthenticatedUserID(session), offset, limit);

	 }

	 /**
	  * Gets a list of sales created by a seller within a given range
	  *
	  * @param seller the user that owns the sales
	  * @return the list of sales
	  */
	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public List<Sale> getSalesBySeller(User user)
	 {
		  return getSalesBySeller(user, 0, Integer.MAX_VALUE);
	 }

	 /**
	  * Gets the sale created by the seller whose ID is provided
	  *
	  * @param sellerID the ID of the seller
	  * @return the list of sales
	  * @throws UserDoesNotExistException if no seller with the matching ID
	  * exists
	  */
	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public List<Sale> getSalesBySellerID(Integer sellerID) throws UserDoesNotExistException
	 {
		  return getSalesBySellerID(sellerID, 0, Integer.MAX_VALUE);
	 }

	 /**
	  * Gets sale for the authenticated user
	  *
	  * @param session The HttpSession object
	  * @return the list of sales
	  * @throws UserNotAuthenticatedException if no authenticated user exists
	  * @throws UserDoesNotExistException if the user authenticated against the
	  * session does not exist. This case would be unusual.
	  */
	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public List<Sale> getSalesByAuthenticatedUser(HttpSession session) throws UserNotAuthenticatedException
	 {
		  return getSalesByAuthenticatedUser(session, 0, Integer.MAX_VALUE);
	 }

	 /**
	  * Gets the sale count by the seller
	  *
	  * @param seller the seller
	  * @return the sale count
	  */
	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public int getSaleCountBySeller(User seller)
	 {

		  String _methodName = "getSaleCountBySeller";

		  TraderLogger.logMethodEntry(_className, _methodName);

		  if (seller == null)
		  {
			   TraderLogger.logWarning(_className, _methodName, "Seller is null");
			   TraderLogger.logMethodExit(_className, _methodName);
			   throw new IllegalArgumentException("seller is null");
		  }


		  TraderLogger.logMethodExit(_className, _methodName);

		  return getSaleCountBySellerID(seller.getId());
	 }

	 /**
	  * Gets sale count started by the seller whose ID matches sellerID
	  *
	  * @param seller the ID of the seller
	  * @return the sale count. If no user found with the seller id, returns 0
	  *
	  */
	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public int getSaleCountBySellerID(Integer sellerID)
	 {
		  String _methodName = "getSalesByUserID";

		  TraderLogger.logMethodEntry(_className, _methodName);

		  if (sellerID == null)
		  {
			   TraderLogger.logWarning(_className, _methodName, "sellerID is null");
			   TraderLogger.logMethodExit(_className, _methodName);
			   throw new IllegalArgumentException("sellerID is null");
		  }

		  TraderLogger.logInfo(_className, _methodName, "Finding sale count by user id: " + sellerID);


		  TypedQuery<Number> querySaleCountBySeller = entityManager.createNamedQuery(SaleEntityConsts.NAME_QUERY_SALE_COUNT_BY_SELLER, Number.class);
		  querySaleCountBySeller.setParameter(SaleEntityConsts.NAME_QUERY_SALE_COUNT_BY_SELLER_PARAM_SELLER_ID, sellerID);


		  int saleCount = querySaleCountBySeller.getSingleResult().intValue();

		  TraderLogger.logInfo(_className, _methodName, "Found sale count: " + saleCount);

		  TraderLogger.logMethodExit(_className, _methodName);

		  return saleCount;
	 }

	 /**
	  * Gets sale count for the authenticated user
	  *
	  * @param session The HttpSession object
	  * @return the sale count
	  * @throws UserNotAuthenticatedException if no authenticated user exists
	  * @throws UserDoesNotExistException if the user authenticated against the
	  * session does not exist. This case would be unusual.
	  */
	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public int getSaleCountByAuthenticatedUser(HttpSession session) throws UserNotAuthenticatedException
	 {

		  return getSaleCountBySellerID(HttpUtils.getAuthenticatedUserID(session));
	 }

	 /**
	  * Gets a sale by a sale ID
	  *
	  * @param saleID the sale ID
	  * @return the sale
	  * @throws SaleDoesNotExistException if no sale with the ID exists
	  */
	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public Sale getSaleByID(Integer saleID) throws SaleDoesNotExistException
	 {
		  final String _methodName = "getSaleByID";

		  TraderLogger.logMethodEntry(_className, _methodName);

		  TraderLogger.logInfo(_className, _methodName, "Trying to find sale with ID " + saleID);

		  if (saleID == null)
		  {
			   TraderLogger.logWarning(_className, _methodName, "Sale ID is null");
			   TraderLogger.logMethodExit(_className, _methodName);
			   throw new IllegalArgumentException("Sale ID is null");
		  }

		  Sale sale = entityManager.find(Sale.class, saleID);

		  if (sale == null)
		  {
			   TraderLogger.logWarning(_className, _methodName, "No sale found with ID " + saleID);
			   TraderLogger.logMethodExit(_className, _methodName);
			   throw new SaleDoesNotExistException(saleID);
		  }

		  TraderLogger.logInfo(_className, _methodName, "Found the sale.");

		  TraderLogger.logMethodExit(_className, _methodName);

		  return sale;
	 }

	 /**
	  * Gets a sale by a sale ID
	  *
	  * @param saleID the sale ID
	  * @return the sale. if no sale exists by the ID, null is returned
	  */
	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public Sale findSaleByID(Integer saleID)
	 {
		  try
		  {
			   return getSaleByID(saleID);
		  }
		  catch (SaleDoesNotExistException ex)
		  {
			   return null;
		  }
	 }

	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public boolean getSaleFavouritedByUser(User user, Sale sale)
	 {
		  final String _methodName = "getSaleFavouritedByUser";

		  TraderLogger.logMethodEntry(_className, _methodName);

		  if (user == null || sale == null)
		  {
			   TraderLogger.logWarning(_className, _methodName, "user or sale is null");
			   TraderLogger.logMethodExit(_className, _methodName);
			   throw new IllegalArgumentException("user or sale is null");
		  }

		  TraderLogger.logInfo(_className, _methodName, String.format("Finding if sale %d favourited by user %d ", sale.getId(), user.getId()));

		  boolean found = user.isSaleFavourited(sale);

		  TraderLogger.logMethodExit(_className, _methodName);

		  if (found)
		  {
			   TraderLogger.logInfo(_className, _methodName, "Sale is favourited by user");
		  }
		  else
		  {
			   TraderLogger.logInfo(_className, _methodName, "Sale is not favourited by user");
		  }
		  return found;
	 }

	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public void favouriteSaleForUser(User user, Sale sale) throws SaleAlreadyFavouritedByUserException
	 {
		  final String _methodName = "favouriteSaleForUser";

		  TraderLogger.logMethodEntry(_className, _methodName);

		  if (user == null || sale == null)
		  {
			   TraderLogger.logWarning(_className, _methodName, "user or sale is null");
			   TraderLogger.logMethodExit(_className, _methodName);
			   throw new IllegalArgumentException("user or sale is null");
		  }

		  TraderLogger.logInfo(_className, _methodName, "Favouriting sale " + sale.getId() + " for user " + user.getId());

		  if (getSaleFavouritedByUser(user, sale))
		  {
			   TraderLogger.logInfo(_className, _methodName, "Sale is already favourited by user");
			   TraderLogger.logMethodExit(_className, _methodName);
			   throw new SaleAlreadyFavouritedByUserException("Sale is already favourited");
		  }

		  sale.addFavouritedUser(user);
		  user.addFavouriteSale(sale);

		  entityManager.merge(user);

		  TraderLogger.logMethodExit(_className, _methodName);
	 }

	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public void unfavouriteSaleForUser(User user, Sale sale) throws SaleNotFavouritedByUserException
	 {
		  final String _methodName = "unfavouriteSaleForUser";

		  TraderLogger.logMethodEntry(_className, _methodName);

		  if (user == null || sale == null)
		  {
			   TraderLogger.logWarning(_className, _methodName, "user or sale is null");
			   TraderLogger.logMethodExit(_className, _methodName);
			   throw new IllegalArgumentException("user or sale is null");
		  }

		  if (!getSaleFavouritedByUser(user, sale))
		  {
			   TraderLogger.logInfo(_className, _methodName, "Sale is not already favourited by user");
			   TraderLogger.logMethodExit(_className, _methodName);
			   throw new SaleNotFavouritedByUserException("Sale is not already favourited");
		  }


		  sale.unfavouriteUser(user);
		  user.unfavouriteSale(sale);

		  entityManager.merge(user);

		  TraderLogger.logMethodExit(_className, _methodName);
	 }

	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public boolean getSaleFavouritedByUser(Integer userId, Integer saleID)
	 {
		  final String _methodName = "getSaleFavouritedByUser";

		  TraderLogger.logMethodEntry(_className, _methodName);

		  if (userId == null || saleID == null)
		  {
			   TraderLogger.logWarning(_className, _methodName, "userId or saleID is null");
			   TraderLogger.logMethodExit(_className, _methodName);
			   throw new IllegalArgumentException("userId or saleID is null");
		  }

		  TraderLogger.logInfo(_className, _methodName, String.format("Finding if sale %d favourited by user %d ", saleID, userId));


		  TypedQuery<Number> querySaleFavourited = entityManager.createNamedQuery(SaleEntityConsts.NAMED_QUERY_FIND_FAVOURITED_SALE_BY_USER, Number.class);

		  querySaleFavourited.setParameter(SaleEntityConsts.NAMED_QUERY_FIND_FAVOURITED_SALE_BY_USER_PARAM_USER_ID, userId);
		  querySaleFavourited.setParameter(SaleEntityConsts.NAMED_QUERY_FIND_FAVOURITED_SALE_BY_USER_PARAM_SALE_ID, saleID);

		  boolean found = querySaleFavourited.getSingleResult().intValue() > 0;

		  if (found)
		  {
			   TraderLogger.logInfo(_className, _methodName, "Sale is favourited by user");
		  }
		  else
		  {
			   TraderLogger.logInfo(_className, _methodName, "Sale is not favourited by user");
		  }

		  TraderLogger.logMethodExit(_className, _methodName);

		  return found;
	 }

	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public List<Sale> getSalesFavouritedByUserID(Integer userID)
	 {
		  return getSalesFavouritedByUserID(userID, 0, Integer.MAX_VALUE);
	 }

	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public List<Sale> getSalesFavouritedByUser(User user)
	 {

		  return getSalesFavouritedByUser(user, 0, Integer.MAX_VALUE);
	 }

	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public List<Sale> getSalesFavouritedByAuthenticatedUser(HttpSession session) throws UserNotAuthenticatedException
	 {
		  return getSalesFavouritedByAuthenticatedUser(session, 0, Integer.MAX_VALUE);
	 }

	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public List<Sale> getSalesFavouritedByUserID(Integer userID, int offset, int limit)
	 {
		  final String _methodName = "getSalesFavouritedByUser";

		  TraderLogger.logMethodEntry(_className, _methodName);

		  if (userID == null)
		  {
			   TraderLogger.logWarning(_className, _methodName, "userId is null");
			   TraderLogger.logMethodExit(_className, _methodName);
			   throw new IllegalArgumentException("userId is null");
		  }

		  TraderLogger.logInfo(_className, _methodName, String.format("Finding all sales favourited by user %d ", userID));


		  TypedQuery<Sale> querySaleFavourited = entityManager.createNamedQuery(SaleEntityConsts.NAMED_QUERY_FIND_FAVOURITED_SALE_LIST_BY_USER, Sale.class);
		  querySaleFavourited.setParameter(SaleEntityConsts.NAMED_QUERY_FIND_FAVOURITED_SALE_LIST_BY_USER_PARAM_USER_ID, userID);
		  querySaleFavourited.setFirstResult(offset);
		  querySaleFavourited.setMaxResults(limit);

		  TraderLogger.logInfo(_className, _methodName, "Found all the sales");

		  TraderLogger.logMethodExit(_className, _methodName);

		  return querySaleFavourited.getResultList();
	 }

	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public List<Sale> getSalesFavouritedByUser(User user, int offset, int limit)
	 {
		  if (user == null)
		  {
			   TraderLogger.logWarning(_className, "getSalesFavouritedByUser", "user is null");
			   throw new IllegalArgumentException("user is null");
		  }

		  return getSalesFavouritedByUserID(user.getId(), offset, limit);
	 }

	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public List<Sale> getSalesFavouritedByAuthenticatedUser(HttpSession session, int offset, int limit) throws UserNotAuthenticatedException
	 {
		  if (session == null)
		  {
			   TraderLogger.logWarning(_className, "getSalesFavouritedByAuthenticatedUser", "session is null");
			   throw new IllegalArgumentException("session is null");
		  }
		  return getSalesFavouritedByUserID(HttpUtils.getAuthenticatedUserID(session), offset, limit);
	 }

	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public int getFavouritedSaleCountByUser(User user)
	 {
		  if (user == null)
		  {
			   TraderLogger.logWarning(_className, "getFavouritedSaleCountByUser", "user is null");
			   throw new IllegalArgumentException("user is null");
		  }

		  return getFavouritedSaleCountByUserID(user.getId());
	 }

	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public int getFavouritedSaleCountByUserID(Integer userID)
	 {
		  String _methodName = "getFavouritedSaleCountByUserID";

		  TraderLogger.logMethodEntry(_className, _methodName);

		  if (userID == null)
		  {
			   TraderLogger.logWarning(_className, _methodName, "userID is null");
			   TraderLogger.logMethodExit(_className, _methodName);
			   throw new IllegalArgumentException("userID is null");
		  }

		  TraderLogger.logInfo(_className, _methodName, "Finding favourite sale count by user id: " + userID);


		  TypedQuery<Number> querySaleFavouritedCountBySeller = entityManager.createNamedQuery(SaleEntityConsts.NAMED_QUERY_FAVOURITED_SALE_COUNT_BY_USER, Number.class);
		  querySaleFavouritedCountBySeller.setParameter(SaleEntityConsts.NAMED_QUERY_FAVOURITED_SALE_COUNT_BY_USER_PARAM_USER_ID, userID);


		  int saleCount = querySaleFavouritedCountBySeller.getSingleResult().intValue();

		  TraderLogger.logInfo(_className, _methodName, "Found sale count: " + saleCount);

		  TraderLogger.logMethodExit(_className, _methodName);

		  return saleCount;
	 }

	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public int getFavouritedSaleCountByAuthenticatedUser(HttpSession session) throws UserNotAuthenticatedException
	 {
		  if (session == null)
		  {
			   TraderLogger.logWarning(_className, "getFavouritedSaleCountByAuthenticatedUser", "session is null");
			   throw new IllegalArgumentException("session is null");
		  }

		  return getFavouritedSaleCountByUserID(HttpUtils.getAuthenticatedUserID(session));
	 }

	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public SaleLookupResult getSalesByQuery(SaleLookupQuery query) throws LocationNotFoundException
	 {
		  return getSalesByQuery(query, 0, Integer.MAX_VALUE);
	 }

	 @Override
	 @TransactionAttribute(TransactionAttributeType.REQUIRED)
	 public SaleLookupResult getSalesByQuery(SaleLookupQuery query, int offset, int limit) throws LocationNotFoundException
	 {
		  final String _methodName = "getSalesByKeyword";

		  TraderLogger.logMethodEntry(_className, _methodName);
		  
		  StringBuffer jpqlQueryImpl = new StringBuffer ("SELECT s from Sale s");

		  boolean addedCondition = false;

		  String queryKeywords = query.getKeywords();

		  if (queryKeywords != null)
		  {
			   TraderLogger.logInfo(_className, _methodName, "Adding condition (keywords): " + queryKeywords);
			   if (!addedCondition)
			   {
				    jpqlQueryImpl.append(" WHERE ");
				    addedCondition = true;
			   }

			   jpqlQueryImpl.append(" SQL('MATCH(DESCRIPTION, TITLE) AGAINST ( ? )', :textSearchTerm) ");

		  }
	
		  ArrayList <SaleCategoryEnum> categories = query.getCategories();

		  if (categories != null)
		  {			  
			   String categoriesS = StringUtils.join(categories, "', '");
			   TraderLogger.logInfo(_className, _methodName, "Adding condition (categories): " + categoriesS);
			   if (!addedCondition)
			   {
				    jpqlQueryImpl.append(" WHERE ");
				    addedCondition = true;
			   }
			   else
			   {
				    jpqlQueryImpl.append(" AND ");
			   }

			   jpqlQueryImpl.append(" s.category IN :saleCategoryList");

		  }

		  Location userEnteredLocation = query.getLocation();

		  if (userEnteredLocation != null)
		  {
			   Country country = userEnteredLocation.getCountry();
			   if (country != null)
			   {
				    String userEnteredCountryName = country.getCountryName();

				    TraderLogger.logInfo(_className, _methodName, "Adding condition (country): " + userEnteredCountryName);

				    if (!addedCondition)
				    {
						jpqlQueryImpl.append(" WHERE ");
						addedCondition = true;
				    }
				    else
				    {
						jpqlQueryImpl.append(" AND ");
				    }

				    jpqlQueryImpl.append(" s.location.country.countryName = :saleCountryName");
			   
				    Region region = userEnteredLocation.getRegion();

				    if (region != null)
				    {
						String userEnteredRegionName = region.getRegionName();

						TraderLogger.logInfo(_className, _methodName, "Adding condition (region): " + userEnteredRegionName);

						jpqlQueryImpl.append(" AND s.location.region.regionName = :saleRegionName");

						String city = userEnteredLocation.getCity();
						if (city != null)
						{
							 TraderLogger.logInfo(_className, _methodName, "Adding condition (city): " + city);

							 jpqlQueryImpl.append(" AND s.location.city = :cityName");
						}
				    }
			   }
		  }
		  		  
		  
		  String jpqlCountQueryImpl = jpqlQueryImpl.toString().replaceFirst("SELECT s", "SELECT COUNT(s.id)");		  
		  String jpqlQuery = jpqlQueryImpl.append(" ORDER BY s.id DESC ").toString();
		  
		  TypedQuery<Number> jpqlCountQuery = entityManager.createQuery(jpqlCountQueryImpl, Number.class);		  
		  TypedQuery<Sale> jpqlTypedQuery = entityManager.createQuery(jpqlQuery, Sale.class);
		  
		  
		  if (queryKeywords != null)
		  {
			   jpqlTypedQuery.setParameter("textSearchTerm", queryKeywords);
			   jpqlCountQuery.setParameter("textSearchTerm", queryKeywords);
		  }
		  
		  
		  if (categories != null)
		  {
			   jpqlTypedQuery.setParameter("saleCategoryList", categories);
			   jpqlCountQuery.setParameter("saleCategoryList", categories);
		  }
		  
		  if (userEnteredLocation != null)
		  {
			   Country country = userEnteredLocation.getCountry();
			   if (country != null)
			   {				    
				    jpqlTypedQuery.setParameter("saleCountryName", country.getCountryName());
				    jpqlCountQuery.setParameter("saleCountryName", country.getCountryName());		

				    
				    Region region = userEnteredLocation.getRegion();

				    if (region != null)
				    {
						jpqlTypedQuery.setParameter("saleRegionName", region.getRegionName());
						jpqlCountQuery.setParameter("saleRegionName", region.getRegionName());							
						
						String city = userEnteredLocation.getCity();
						if (city != null)
						{
							 jpqlTypedQuery.setParameter("cityName", city);
							 jpqlCountQuery.setParameter("cityName", city);							 
						}
				    }
			   }

		  }
		  
		  jpqlTypedQuery.setFirstResult(offset);
		  jpqlTypedQuery.setMaxResults(limit);
		  
		  int saleCount = jpqlCountQuery.getSingleResult().intValue();
		  List<Sale> result = (List<Sale>) jpqlTypedQuery.getResultList();

		  SaleLookupResult searchResult = new SaleLookupResult(result, saleCount);

		  TraderLogger.logInfo(_className, _methodName, "Page results = " + result.size() + ", total results = " + saleCount);
		  
		  TraderLogger.logMethodExit(_className, _methodName);

		  return searchResult;
	 }

}
